/*
 * $Id: PDFDocument.java,v 1.2 2001/11/15 20:18:11 ezb Exp $
 *
 * $Date: 2001/11/15 20:18:11 $
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 */
package gnu.jpdf;

import java.io.*;
import java.util.*;

/**
 * <p>This class is the base of the PDF generator. A PDFDocument class is 
 * created for a document, and each page, object, annotation, 
 * etc is added to the document.
 * Once complete, the document can be written to an OutputStream, and the PDF
 * document's internal structures are kept in sync.</p>
 *
 * <p>Note that most programmers using this package will NEVER access 
 * one of these objects directly.  Most everything can be done using 
 * <code>PDFJob</code> and <code>PDFGraphics</code>, so you don't need 
 * to directly instantiate a <code>PDFDocument</code></p>  
 *
 * <p>ezb - 20011115 - Wondering if the constructors should even be public.
 * When would someone want to make one of these and manipulate it outside 
 * the context of a job and graphics object?</p>
 *
 * @author Peter T Mount, http://www.retep.org.uk/pdf/
 * @author Eric Z. Beard, ericzbeard@hotmail.com
 * @author $Author: ezb $
 * @version $Revision: 1.2 $, $Date: 2001/11/15 20:18:11 $
 */
public class PDFDocument implements Serializable
{

	private static final long serialVersionUID = 1L;

	/*
	 * NOTE: This class originated in uk.org.retep.pdf, by Peter T. Mount, and it 
	 * has been modified by Eric Z. Beard, ericzbeard@hotmail.com.  The 
	 * package name was changed to gnu.jpdf and several inner classes were 
	 * moved out into their own files.
	 */

	/**
	 * This is used to allocate objects a unique serial number in the document.
	 */
	protected int objser;

	/**
	 * This vector contains each indirect object within the document.
	 */
	protected Vector<PDFObject> objects;

	/**
	 * This is the Catalog object, which is required by each PDF Document
	 */
	private PDFCatalog catalog;

	/**
	 * This is the info object. Although this is an optional object, we
	 * include it.
	 */
	private PDFInfo info;

	/**
	 * This is the Pages object, which is required by each PDF Document
	 */
	private PDFPageList pdfPageList;

	/**
	 * This is the Outline object, which is optional
	 */
	private PDFOutline outline;

	/**
	 * This holds a PDFObject describing the default border for annotations.
	 * It's only used when the document is being written.
	 */
	protected PDFObject defaultOutlineBorder;

	/**
	 * <p>This page mode indicates that the document 
	 * should be opened just with the page visible.  This is the default</p>
	 */
	public static final int USENONE = 0;

	/**
	 * <p>This page mode indicates that the Outlines 
	 * should also be displayed when the document is opened.</p>
	 */
	public static final int USEOUTLINES = 1;

	/**
	 * <p>This page mode indicates that the Thumbnails should be visible when the
	 * document first opens.</p>
	 */
	public static final int USETHUMBS = 2;

	/**
	 * <p>
	 * This page mode indicates that when the document is opened, it is displayed
	 * in full-screen-mode. There is no menu bar, window controls nor any other
	 * window present.</p>
	 */
	public static final int FULLSCREEN = 3;

	/**
	 * <p>
	 * These map the page modes just defined to the pagemodes setting of PDF.
	 * </p>
	 */
	public static final String PDF_PAGE_MODES[] = {
		"/UseNone",
		"/UseOutlines",
		"/UseThumbs",
		"/FullScreen"
	};

	/**
	 * This is used to provide a unique name for a font
	 */
	private int fontid = 0;

	/**
	 * <p>This is used to provide a unique name for an image</p>
	 */
	private int imageid = 0;

	/**
	 * This holds the current fonts
	 */
	private Vector<PDFFont> fonts;


	/**
	 * <p>This creates a PDF document with the default pagemode</p>
	 */
	public PDFDocument() {
		this(USENONE);
	}

	/**
	 * <p>This creates a PDF document</p>
	 * @param pagemode an int, determines how the document will present itself to
	 *        the viewer when it first opens.
	 */
	public PDFDocument(int pagemode) {
		objser = 1;
		objects = new Vector<PDFObject>();
		fonts = new Vector<PDFFont>();

		// Now create some standard objects
		add(pdfPageList = new PDFPageList());
		add(catalog = new PDFCatalog(pdfPageList,pagemode));
		add(info = new PDFInfo());
		// Acroread on linux seems to die if there is no root outline
		add(getOutline());
	}

	/**
	 * This adds a top level object to the document.
	 *
	 * <p>Once added, it is allocated a unique serial number.
	 *
	 * <p><b>Note:</b> Not all object are added directly using this method.
	 * Some objects which have Kids (in PDF sub-objects or children are
	 * called Kids) will have their own add() method, which will call this
	 * one internally.
	 *
	 * @param obj The PDFObject to add to the document
	 * @return the unique serial number for this object.
	 */
	public synchronized int add(PDFObject obj)
	{
		objects.addElement(obj);
		obj.objser=objser++; // create a new serial number
		obj.pdfDocument = this;  // so they can find the document they belong to
		// If its a page, then add it to the pages collection
		if(obj instanceof PDFPage)
			pdfPageList.add((PDFPage)obj);
		return obj.objser;
	}

	/**
	 * <p>This returns a specific page. It's used mainly when using a
	 * Serialized template file.</p>
	 *
	 * ?? How does a serialized template file work ???
	 *
	 * @param page page number to return
	 * @return PDFPage at that position
	 */
	public PDFPage getPage(int page)
	{return pdfPageList.getPage(page);}

	/**
	 * @return the root outline
	 */
	public PDFOutline getOutline()
	{
		if(outline==null) 
		{
			outline = new PDFOutline();
			catalog.setOutline(outline);
		}
		return outline;
	}

	/**
	 * This returns a font of the specified type and font. If the font has
	 * not been defined, it creates a new font in the PDF document, and
	 * returns it.
	 *
	 * @param type PDF Font Type - usually "/Type1"
	 * @param font Java font name
	 * @param style java.awt.Font style (NORMAL, BOLD etc)
	 * @return PDFFont defining this font
	 */
	public PDFFont getFont(String type,String font,int style) 
	{
		for(Enumeration en = fonts.elements(); en.hasMoreElements(); )
		{
			PDFFont ft = (PDFFont) en.nextElement();
			if(ft.equals(type,font,style))
				return ft;
		}

		// the font wasn't found, so create it
		fontid++;
		PDFFont ft = new PDFFont("/F"+fontid,type,font,style);
		add(ft);
		fonts.addElement(ft);
		return ft;
	}

	/**
	 * Sets a unique name to a PDFImage
	 * @param img PDFImage to set the name of
	 * @return the name given to the image
	 */
	public String setImageName(PDFImage img) 
	{
		imageid++;
		img.setName("/Image"+imageid);
		return img.getName();
	}

	/**
	 * <p>Set the PDFInfo object, which contains author, title, 
	 * keywords, etc</p>
	 */
	public void setPDFInfo(PDFInfo info) 
	{this.info = info;}

	/**
	 * <p>Get the PDFInfo object, which contains author, title, keywords,
	 * etc</p>
	 */
	public PDFInfo getPDFInfo() 
	{return this.info;}

	/**
	 * This writes the document to an OutputStream.
	 *
	 * <p><b>Note:</b> You can call this as many times as you wish, as long as
	 * the calls are not running at the same time.
	 *
	 * <p>Also, objects can be added or amended between these calls.
	 *
	 * <p>Also, the OutputStream is not closed, but will be flushed on
	 * completion. It is up to the caller to close the stream.
	 *
	 * @param os OutputStream to write the document to
	 * @exception IOException on error
	 */
	public void write(OutputStream os) throws IOException
	{
		PDFOutput pos = new PDFOutput(os);

		// Write each object to the OutputStream. We call via the output
		// as that builds the xref table
		for(Enumeration en = objects.elements(); en.hasMoreElements(); )
			pos.write((PDFObject)en.nextElement());

		// Finally close the output, which writes the xref table.
		pos.close();

		// and flush the output stream to ensure everything is written.
		os.flush();
	}
} // end class PDFDocument